﻿// 复合文档操作

namespace compound_doc
{
    using System;
    using System.IO;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using ComTypes = System.Runtime.InteropServices.ComTypes;
    using com_type;
    
    /// <summary>
    /// 结构化存储文件类
    /// </summary>
    public static class StorageFile
    {
        public static bool IsStorageFile(string sFileName)
        {
            if(sFileName == null || !System.IO.File.Exists(sFileName))
            {
                return false;
            }
            int ret = StorageFunc.StgIsStorageFile(sFileName);
            if (ret == HRESULT.S_OK)
            {
                return true;
            }
            else
            {
                return false;
            }
        }

        public static Storage OpenStorageFile(string storageFile, STGM mode)
        {
            if (!IsStorageFile(storageFile))
            {
                return null;
            }

            try
            {
                IStorage storage = StorageFunc.StgOpenStorage(storageFile, IntPtr.Zero, (uint)mode, IntPtr.Zero, 0);
                return new Storage(storage);
            }
            catch (COMException)
            {
                return null;
            }
        }
    }

    public sealed class Stream :System.IDisposable
    {
        private bool disposed;
        private IStream stream;

        internal Stream(IStream stream)
        {
            this.stream = stream;
        }

        ~Stream()
        {
            this.Dispose();
        }

        public void Dispose()
        {
            if (!this.disposed)
            {
                Marshal.ReleaseComObject(this.stream);
                this.stream = null;

                this.disposed = true;
            }

            GC.Collect();
            GC.WaitForPendingFinalizers();
            //GC.SuppressFinalize(this);
        }

        public int Read(byte[] buffer, int count)
        {
            if (stream == null)
            {
                //throw new ObjectDisposedException("Invalid stream object.");
                return -1;
            }

            IntPtr address = new IntPtr();
            stream.Read(buffer, count, address);

            return count;
        }
    }

    /// <summary>
    /// 存储类
    /// </summary>
    public class Storage : System.IDisposable
    {
        private bool disposed;
        private IStorage storage;

        internal Storage(IStorage storage)
        {
            this.storage = storage;
        }

        ~Storage()
        {
            this.Dispose();
        }

        public void Dispose()
        {
            if (!this.disposed)
            {
                Marshal.ReleaseComObject(this.storage);
                this.storage = null;

                this.disposed = true;
            }
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        public Storage CreateStorage(string name, STGM mode)
        {
            if (name == null)
            {
                return null;
            }

            IStorage subStorage = null;
            try
            {
                //子点节，总是建议使用独占模式
                this.storage.CreateStorage(name, (uint)mode, 0, 0, out subStorage);
                this.storage.Commit(0);

                return new Storage(subStorage);
            }
            catch (COMException ex)
            {
                if (subStorage != null)
                {
                    Marshal.ReleaseComObject(subStorage);
                }
                tool.LogTool.GetInstance().AddLog(tool.LogTool.LOG_TYPE.EXCEPTION, null, "CreateStorage", ex);
            }

            return null;
        }

        /// <summary>
        /// 打开一个子存储
        /// </summary>
        /// <param name="name">存储名称</param>
        /// <returns></returns>
        public Storage OpenStorage(string name, STGM mode)
        {
            if(name == null)
            {
                return null;
            }

            IStorage subStorage = null;
            try
            {
                //子点节，总是建议使用独占模式
                this.storage.OpenStorage(name, null, (uint)mode, IntPtr.Zero, 0, out subStorage);
                return new Storage(subStorage);
            }
            catch (COMException)
            {
                if (subStorage != null)
                {
                    Marshal.ReleaseComObject(subStorage);
                }
                return null;
            }
        }

        public Stream CreateStream(string name, STGM mode)
        {
            if(name == null)
            {
                return null;
            }

            IStream subStream = null;

            try
            {
                //总是建议使用独占模式
                this.storage.CreateStream(name, (uint)mode, 0, 0, out subStream);
                return new Stream(subStream);
            }
            catch (COMException)
            {
                if (subStream != null)
                {
                    Marshal.ReleaseComObject(subStream);
                }
                return null;
            }
        }

        public Stream OpenStream(string name, STGM mode)
        {
            if(name == null)
            {
                return null;
            }

            IStream subStream = null;
            try
            {
                //总是建议使用独占模式
                this.storage.OpenStream(name, IntPtr.Zero, (uint)mode, 0, out subStream);
                return new Stream(subStream);
            }
            catch (COMException)
            {
                if (subStream != null)
                    Marshal.ReleaseComObject(subStream);

                return null;
            }
        }

        public void EnumElements(out compound_doc.com_type.IEnumSTATSTG statStg)
        {
            this.storage.EnumElements(0, IntPtr.Zero, 0, out statStg);
            statStg.Reset();
        }

        public void Stat(out ComTypes.STATSTG statStg, uint flag)
        {
            this.storage.Stat(out statStg, flag);
        }
    }

    // ----Begin----
    // 操作Excel二进制文件帮助类
    // 节点类型
    public enum NODE_TYPE
    {
        STORAGE,
        STREAM
    }

    public class CNode
    {
        protected string m_sName;
        protected NODE_TYPE m_type;

        public CNode(string sName, NODE_TYPE nt)
        {
            m_sName = sName;
            m_type = nt;
        }

        public string GetName()
        {
            return m_sName;
        }

        public NODE_TYPE GetNodeType()
        {
            return m_type;
        }
    }

    public class CStorageNode : CNode
    {
        protected List<CNode> m_nodeList;

        public CStorageNode(string sName) :
            base(sName, NODE_TYPE.STORAGE)
        {
            m_nodeList = new List<CNode>();
        }

        public void AddNode(CNode node)
        {
            m_nodeList.Add(node);
        }

        public int GetNodeListSize()
        {
            return m_nodeList.Count;
        }

        public CNode GetNodeByIndex(int i)
        {
            return m_nodeList[i];
        }
    }

    public class CStreamNode : CNode
    {
        protected int m_nSize;
        protected byte[] m_data;
        public CStreamNode(string sName, int nSize) :
            base(sName, NODE_TYPE.STREAM)
        {
            m_nSize = nSize;
            m_data = new byte[nSize];
        }

        public void SetData(byte[] data)
        {
            m_data = data;
        }

        public byte[] GetData()
        {
            return m_data;
        }

        public int GetSize()
        {
            return m_nSize;
        }
    }

    // 枚举Storage节点
    public class CEnumStorage
    {
        protected Storage m_storage;
        internal com_type.IEnumSTATSTG m_enumSTATSTG;
        ComTypes.STATSTG m_statstg;
        public CEnumStorage()
        {
            m_enumSTATSTG = null;
        }

        public void Init(Storage storage)
        {
            m_storage = storage;

            m_storage.Stat(out m_statstg, (uint)STATFLAG.DEFAULT);

            m_storage.EnumElements(out m_enumSTATSTG);
        }

        public int Next(ref int stgty, ref CNode ppNode)
        {
            ComTypes.STATSTG[] stgs = { m_statstg };
            uint fetched = 0;
            int hr = 0;
            hr = m_enumSTATSTG.Next(1, stgs, out fetched);
            if (hr == 0)
            {
                ComTypes.STATSTG stg = stgs[0];

                stgty = stg.type;
                if (stgty == (int)STGTY.STORAGE)
                {
                    ppNode = new CStorageNode(stg.pwcsName);
                }
                else if (stgty == (int)STGTY.STREAM)
                {
                    ppNode = new CStreamNode(stg.pwcsName, (int)stg.cbSize);
                }
            }

            return hr;
        }
    }

    // 枚举Stream节点
    public class CEnumRec
    {
        protected byte[] m_data;    //  存放数据
        protected int m_current;    //  已经遍历到的位置
        public void Init(byte[] data)
        {
            //  前4个字节是该流的标志信息，如Workbook
            m_current = 0;
            m_data = data;
        }

        // 遍历一个记录得到这个记录的数据，大小和ID
        public bool NextRec(ref byte[] ppData,
            ref ushort uSize, ref ushort uMark)
        {
            bool bRet;
            while (bRet = GetNextRecord(ref ppData, ref uSize, ref uMark))
            {
                if (0 != uMark)
                {
                    break;
                }
            }

            return bRet;
        }

        // 遍历一个记录
        protected bool GetNextRecord(ref byte[] ppData,
            ref ushort uSize, ref ushort uMark)
        {
            if (m_current + 4 > m_data.Length)
            {
                return false;
            }

            // 2 bytes record id
            uMark = BitConverter.ToUInt16(m_data, m_current);
            // 2 bytes record size
            uSize = BitConverter.ToUInt16(m_data, m_current + 2);
            ppData = null;
            if (uSize > 0)
            {
                ppData = new byte[uSize];
                Array.Copy(m_data, m_current + 4, ppData, 0, uSize);
            }
            m_current += (4 + uSize);
            return true;
        }
    }
    // ----End----
    // 操作Excel二进制文件帮助类

    public class CBiffHlp
    {
        public static void DecodeString(byte[] buf, ref int iPos, int cch,
            byte grbitCHR, out string str)
        {
            str = null;
            if (cch <= 0)
            {
                return; //  BLank
            }

            const byte CHR_GRBIT_UNCOMPRESSED = 0x01;
            if (0 != (CHR_GRBIT_UNCOMPRESSED & grbitCHR))
            {
                byte[] tmp = new byte[cch * 2];
                Array.Copy(buf, iPos, tmp, 0, cch * 2);
                str = System.Text.Encoding.Unicode.GetString(tmp);
                iPos += cch * 2;
            }
            else
            {
                byte[] tmp = new byte[cch];
                Array.Copy(buf, iPos, tmp, 0, cch);
                str = System.Text.Encoding.ASCII.GetString(tmp);
                iPos += cch;
            }
        }

        public static void DecodeBuffer(byte[] buf, ref int iPos, int cce)
        {
            if(cce <= 0)
            {
                return;
            }
            for (int i = 0; i < cce; ++i)
            {
                ++iPos;
            }
        }
    }
}